<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

/**
 * Toc class
 * 
 * The Table of Contents creator for CI Docs
 * 
 * @package CI Docs
 * @version 0.1b
 * @author chadwithuhc
 * @link http://chadwithuhc.com
 * @copyright 2009 chadwithuhc
 * 
 */
class Toc {

	var $ci;
	
	/**
	 * $contents
	 * 
	 * Holds the final output for the Table of Contents
	 * 
	 * @var string
	 * @access public
	 */
	var $contents;
	
	/**
	 * $col_count
	 * 
	 * Keeps track of the number of cols for width
	 * 
	 * @var mixed
	 * @access public
	 */
	var $col_count = 0;
	
	/**
	 * $categories
	 * 
	 * Holds all the categories and their names / urls
	 * 
	 * (default value: array())
	 * 
	 * @var array
	 * @access public
	 */
	var $categories = array('id' => array(), 'name' => array(), 'url' => array(), 'uri' => array());
	
	/**
	 * $category_items
	 * 
	 * Holds all the categories items and their names, cat_id, and urls
	 * 
	 * (default value: array())
	 * 
	 * @var array
	 * @access public
	 */
	var $category_items = array('id' => array(), 'cat_id' => array(), 'name' => array(), 'url' => array(), 'uri' => array());
	
	/**
	 * Toc ()
	 * 
	 * Default constructor. Gets CI instance.
	 * 
	 * @access public
	 * @return void
	 */
	function Toc() {
		$this->ci =& get_instance();
	}
	
	
	/**
	 * start (  )
	 * 
	 * Start the table of contents.
	 * 
	 * @access public
	 * @return Toc
	 */
	function start() {
		$this->col_count = 0;
		
		// html output
		$class_home = ($this->ci->uri->uri_string() == blank OR $this->ci->uri->uri_string() == $this->ci->docs->config('guide_url')) ? ' class="current"' : blank;
		$class_toc = (strpos($this->ci->uri->uri_string(), $this->ci->docs->config('toc_uri'))) ? ' class="current"' : blank;
		$this->contents =
			'<table cellpadding="0" cellspacing="10" border="0" width="98%">'
			. nl . str_repeat(tb, 3) . '<tr>'
			. nl . str_repeat(tb, 4) . '<td valign="top" width="{width}">'
			. nl . str_repeat(tb, 5) . '<ul class="two_links">'
			. nl . str_repeat(tb, 6) . '<li' . $class_home . '><a href="' . $this->ci->docs->config('guide_home_url') . '">' . lang('user_guide_home') . '</a></li>'
			. nl . str_repeat(tb, 6) . '<li' . $class_toc . '><a href="' . $this->ci->docs->config('toc_url') . '">' . lang('toc_title') . '</a></li>'
			. nl . str_repeat(tb, 5) . '</ul>';
			
		// array output
		// current category is _root
		$this->categories['id'][] = count($this->categories['id']);
		$this->categories['name'][] = '_root';
		$this->categories['url'][] = $this->ci->docs->config('guide_home_url') . $this->_version();
		$this->categories['uri'][] = $this->ci->docs->config('guide_uri');
		$this->current_category = array('id' => end($this->categories['id']), 'name' => end($this->categories['name']), 'url' => end($this->categories['url']), 'uri' => end($this->categories['uri']));
		// user guide home
		$this->category_items['id'][] = count($this->category_items['id']);
		$this->category_items['cat_id'][] = $this->current_category['id'];
		$this->category_items['name'][] = lang('user_guide_home');
		$this->category_items['url'][] = $this->ci->docs->config('guide_home_url') . $this->_version();
		$this->category_items['uri'][] = $this->ci->docs->config('guide_uri');
		// table of contents
		$this->category_items['id'][] = count($this->category_items['id']);
		$this->category_items['cat_id'][] = $this->current_category['id'];
		$this->category_items['name'][] = lang('toc_title');
		$this->category_items['url'][] = $this->ci->docs->config('guide_url') . $this->_version() . $this->ci->docs->config('toc_uri') . suffix;
		$this->category_items['uri'][] = $this->ci->docs->config('toc_uri');
		
		return $this;
	}
	
	
	/**
	 * col (  )
	 * 
	 * Write a new col to the table of contents
	 * 
	 * @access public
	 * @return Toc
	 */
	function col() {
		if ($this->col_count > 0) {
			$this->_close_list();
			$this->contents .= nl . str_repeat(tb, 4) . '</td>';
			$this->contents .= nl . str_repeat(tb, 4) . '<td valign="top"  class="td_sep" width="{width}">';
		}
		
		$this->col_count++;
		return $this;
	}
	
	
	/**
	 * category ( $name, $url = null )
	 * 
	 * Write a new category to the table of contents
	 * 
	 * @access public
	 * @param string $name
	 * @param string $url (default: null)
	 * @return Toc
	 */
	function category($name, $url = null) {
		if (is_null($url)) {
			$url = url_title($name, und, TRUE);
		}
		
		// html output
		$name = $this->ci->docs->lang_prefix($name);
		$this->_close_list();
		$this->contents .= nl . str_repeat(tb, 5) . "<h3>$name</h3>" . nl . str_repeat(tb, 5) . "<ul>";
		
		// array output
		if (!in_array($name, $this->categories['name'])) {
			$this->categories['id'][] = count($this->categories['id']);
			$this->categories['name'][] = $name;
			$this->categories['url'][] = $this->ci->docs->config('guide_url') . $this->_version() . $url . ds;
			$this->categories['uri'][] = $url;
		}
		$this->current_category = array('id' => end($this->categories['id']), 'name' => $name, 'url' => end($this->categories['url']), 'uri' => $url);
		return $this;
	}
	
	
	/**
	 * item ( $name, $url = null )
	 * 
	 * Write a new item to the table of contents
	 * 
	 * @access public
	 * @param string $name
	 * @param string $url (default: null)
	 * @return Toc
	 */
	function item($name, $url = null) {
		if (is_null($url)) {
			$url = url_title($name, und, TRUE);
		}
		
		$uri = $url;
		$url = $this->current_category['url'] . $url . suffix;

		// html output
		$name = $this->ci->docs->lang_prefix($name);
		$class = (index . $this->ci->uri->uri_string() == $url) ? ' class="current"' : blank;
		$this->contents .= nl . str_repeat(tb, 6) . "<li$class><a href=\"$url\">$name</a></li>";
		
		//array output
		if (!in_array($url, $this->category_items['url'])) {
			$this->category_items['id'][] = count($this->category_items['id']);
			$this->category_items['cat_id'][] = $this->current_category['id'];
			$this->category_items['name'][] = $name;
			$this->category_items['url'][] = $url;
			$this->category_items['uri'][] = $uri;
		}
		return $this;
	}
	
	
	/**
	 * items ( $items )
	 * 
	 * Pass in an array of items instead of one at a time
	 * 
	 * @access public
	 * @param array $items
	 * @return Toc
	 */
	function items($items) {
		foreach ($items as $k => $v) {
			$this->item($k, $v);
		}
		return $this;
	}
	
	
	/**
	 * _close_list (  )
	 * 
	 * Finds out whether or not a ul is still open, and if it is close it.
	 * 
	 * @access private
	 * @return void
	 */
	function _close_list() {
		if (preg_match('/\<\/li\>/', substr($this->contents, -10))) {
			$this->contents .= nl . str_repeat(tb, 5) . '</ul>';
		}
	}
	
	
	/**
	 * end (  )
	 * 
	 * End the table of contents which ends up finalizing it too
	 * 
	 * @access public
	 * @return string
	 */
	function end() {
		$this->contents .= nl . str_repeat(tb, 5) . '</ul>' . nl . str_repeat(tb, 4) . '</td>' . nl . str_repeat(tb, 3) . '</tr>' . nl . str_repeat(tb, 2) . '</table>' . nl;
		$this->col_width = round(100 / $this->col_count, 2) . '%';
		$this->contents = str_replace('{width}', $this->col_width, $this->contents);
		return $this->contents;
	}
	
	
	/**
	 * _version (  )
	 * 
	 * Gets the version we are using if we are using version support
	 * 
	 * @access private
	 * @return void
	 */
	function _version() {
		if ($this->ci->docs->config('guide_version') != $this->ci->docs->config('guide_latest_version')) {
			return $this->ci->docs->config('guide_version') . ds;
		}
		return blank;
	}
	
	
	/**
	 * is_toc ( $item = null )
	 * 
	 * Is the page we're on the table of contents. Used for routing.
	 * 
	 * @access public
	 * @return bool
	 */
	function is_toc($item = null) {
		if ($this->ci->uri->uri_string() == blank) {
			return false;
		}
		// if we didnt pass in an item, use the uri string
		if (is_null($item)) {
			$item = index . $this->ci->uri->uri_string();
		}
		return ($item . suffix == $this->category_items['url'][1]);
	}
	
	
	/**
	 * generate (  )
	 * 
	 * Generates the Table of Contents so its ready for retrieval.
	 * 
	 * @access public
	 * @return void
	 */
	function generate() {
		$this->ci->load->file($this->ci->docs->config('toc_menu_path'), TRUE);
		// debug
		//echo '<pre>', print_r($this->categories) . print_r($this->category_items), '</pre>';
	}
	
	
	/**
	 * regenerate (  )
	 * 
	 * Used to regenerate the Table of Contents (in case a version is being used)
	 * 
	 * @access public
	 * @return void
	 */
	function regenerate() {
		$this->contents = null;
		$this->categories = array('id' => array(), 'name' => array(), 'url' => array(), 'uri' => array());
		$this->category_items = array('id' => array(), 'cat_id' => array(), 'name' => array(), 'url' => array(), 'uri' => array());
		$this->generate();
	}
	
	
	/**
	 * get_toc (  )
	 * 
	 * Gets the table of contents. For use once its already been generated.
	 * 
	 * @access public
	 * @return string
	 */
	function get_toc() {
		// debug
		//echo '<pre>' . $this->ci->uri->uri_string() . sp . $this->category_items['url'][1], print_r($this->categories) . print_r($this->category_items), '</pre>';
		return $this->contents;
	}
	
	
	/**
	 * get_breadcrumb (  )
	 * 
	 * Gets the breadcrumb nav
	 * 
	 * @access public
	 * @return string
	 */
	function get_breadcrumb() {
		$data = $this->_get_page_data();
		
		// guide home url
		$bc = "<a href=\"" . $this->ci->docs->config('guide_home_url') . "\">" . $this->ci->docs->vars('guide_user_guide') . "</a>";
				
		/* category homepage support */
		// if including category homepages
		//if ($this->ci->docs->config('include_category_homepages')) {
		//	if ($data['cat_name'] != blank) {
		//		$bc .= bcsp . "<a href=\"" . $data['cat_url'] . "\">" . $data['cat_name'] . "</a>";
		//	}
		//	$bc .= bcsp . $data['name'];
		//}
		/* end category homepage support */
		// otherwise last crumb
		//else {
			if ($data['cat_name'] != blank) {
				$data['cat_name'] .= lang('category_topic_sep') . nbsp;
			}
			$bc .= bcsp . $data['cat_name'] . $data['name'];
		//}
		
		// if we want the advanced breadcrumb, add the next / prev links
		if ($this->ci->docs->config('use_advanced_breadcrumb')) {
			$sep = ($this->prev_topic() == blank OR $this->next_topic() == blank) ? blank : lang('prev_next_sep');
			$bc .= '</p>' . nl . tb . '<p id="breadcrumb_advanced" class="position_' . $this->ci->docs->config('advanced_breadcrumb_position') . '">'
						. trim($this->prev_topic(), ftsp) . $sep . trim($this->next_topic(), ftsp);
		}
		
		return $this->ci->docs->vars('breadcrumb_nav', $bc);
	}
	
	
	/**
	 * get_page_title (  )
	 * 
	 * Gets the title of the page
	 * 
	 * @access public
	 * @return string
	 */
	function get_page_title() {
		$data = $this->_get_page_data();
		$cat = ($data['cat_name'] == blank) ? blank : $data['cat_name'] . ptsp;
		switch ($this->ci->docs->config('title_format')) {
			case 'normal':
				return $this->ci->docs->config('guide_name') . sp . lang('user_guide') . ptsp . $cat . $data['name'];
			case 'reverse':
				return $data['name'] . ptsp . $cat . $this->ci->docs->config('guide_name') . sp . lang('user_guide');
			case 'name_only':
			default:
				return $this->ci->docs->config('guide_name') . sp . lang('user_guide');
		}
		
	}
	
	
	/**
	 * get_title (  )
	 * 
	 * Used to get the h1 title of the page we're on
	 * 
	 * @access public
	 * @return string
	 */
	function get_title() {
		$data = $this->_get_page_data();
		return $data['name'];
	}
	
	
	/**
	 * _get_page_id ( $url = null, $adjust = null )
	 * 
	 * Gets the page id for matching in the array
	 * 
	 * @access private
	 * @param string $adjust
	 * @return int
	 */
	function _get_page_id($url = null, $adjust = null, $fail_gracefully = false) {
		// if an id is not passed in get it by the uri
		if (is_null($url)) {
			$url = $this->ci->uri->uri_string();
			// if its the root url
			if ($url == blank) {
				$url = ds;
			}
			else {
				$url = index . $url;
			}
		}
		
		// suffix support
		if ($url != ds) {
			$url .= suffix;
		}
		
		// find the matching url in table of contents
		foreach ($this->category_items['url'] as $k => $v) {
			if (trim($v, ds) == trim($url, ds)) {
				$id = $k;
			}
		}
		
		// if no match
		if (!isset($id)) {	
			// fail gracefully for linking
			if ($fail_gracefully == true)
				return -1;
			else /* !todo: change to 404 if production site */
				show_error("The url [ $url ] does not exist in your TOC"); //show_404($url);
		}
		
		// for prev and next topic support
		if ($adjust == '++') {
			$id++;
		}
		elseif ($adjust == '--') {
			$id--;
		}

		return $id;
	}
	
	
	/**
	 * _get_page_data ( $id )
	 * 
	 * Gets the pages data based on the id. Stored in the toc arrays categories and category_items
	 * Returns as an array with cat_id, cat_name, cat_url, name, url, id
	 * 
	 * @access private
	 * @param int $id
	 * @return array
	 */
	function _get_page_data($id = null, $fail_gracefully = false) {
		// get the page id automatically
		if (is_null($id))
			$id = $this->_get_page_id(null, null, $fail_gracefully);
		// get the page by url
		elseif (!is_numeric($id))
			$id = $this->_get_page_id($id, null, $fail_gracefully);
		
		// fail greacefully: nothing found in TOC returns false
		if ($id == -1)
			return false;
	
		$data['cat_id'] = $this->category_items['cat_id'][$id];
		$data['cat_name'] = ($this->categories['name'][$data['cat_id']] == '_root') ? blank : $this->categories['name'][$data['cat_id']];
		$data['cat_url'] = $this->categories['url'][$data['cat_id']];
		$data['name'] = $this->category_items['name'][$id];
		$data['url'] = $this->category_items['url'][$id];
		$data['id'] = $id;
		
		return $data;
	}
	
	
	/**
	 * next_topic (  )
	 * 
	 * Gets the next topic link if available
	 * 
	 * @access public
	 * @return string
	 */
	function next_topic() {
		$id = $this->_get_page_id(null, '++');
		if ($id < count($this->category_items['id'])) {
			$data = $this->_get_page_data($id);
			if ($data['cat_name'] != blank) {
				$data['cat_name'] .= lang('category_topic_sep') . nbsp;
			}
		}
		
		if (isset($data)) {
			$output = ftsp . lang('next_topic') . str_repeat(nbsp, 2)
				. "<a href=\"" . $data['url'] . "\" title=\"" . trim($data['cat_name']) . $data['name'] . "\">"
				. (($this->ci->docs->config('show_cat_name_in_topic')) ? $data['cat_name'] : blank) . $data['name']
				. "</a>";
		}
		return (isset($output)) ? $output : blank;
	}
	
	
	/**
	 * prev_topic (  )
	 * 
	 * Gets the previous topic link if available
	 * 
	 * @access public
	 * @return string
	 */
	function prev_topic() {
		$id = $this->_get_page_id(null, '--');
		if ($id >= 0) {
			$data = $this->_get_page_data($id);
			if ($data['cat_name'] != blank) {
				$data['cat_name'] .= lang('category_topic_sep') . nbsp;
			}
		}
		
		if (isset($data)) {
			$output = lang('prev_topic') . str_repeat(nbsp, 2)
				. "<a href=\"" . $data['url'] . "\" title=\"" . trim($data['cat_name']) . $data['name'] . "\">"
				. (($this->ci->docs->config('show_cat_name_in_topic')) ? $data['cat_name'] : blank) . $data['name']
				. "</a>"	. ftsp;
		}
		return (isset($output)) ? $output : blank;
	}
	
	
	
	/**
	 * link ( $url = null, $text = null, $target = '_self', $version = blank, $guide = null )
	 * 
	 * Creates a link to another page. Allows a version, guide, or _root page also.
	 * 
	 * @access public
	 * @param string $url (default: null)
	 * @param string $text (default: null)
	 * @param string $target (default: '_self')
	 * @param string $version (default: null)
	 * @param string $guide (default: null)
	 * @return string
	 */
	function link($url = null, $text = null, $target = '_self', $version = null, $guide = null) {
		// set blanks to nulls, for the lazy people
		$blanks = array('url', 'text', 'guide', 'version');
		foreach ($blanks as $item) {
			if ($$item == blank) {
				$$item = null;
			}
		}
		
		// set default text if null
		if (is_null($text)) {
			$text = lang('default_link_text');
		}
		$text = $this->ci->docs->lang_prefix($text);
	
		// if version support is off you can't link to a version
		if ((!is_null($version) AND $version != 'latest')) {
			$version .= ds;
		}
		else {
			$version = blank;
		}
		
		// if different guide set
		if (!is_null($guide)) {
			$guide = $this->ci->docs->lang_prefix($guide);
			$_guide = $guide;
			$guide = ds . url_title($guide, und);
		}
		else {
			$guide = $this->ci->docs->config('guide_url');
		}
		
		// break apart the url
		if (strpos($url, ds)) {
			$_arr = split(ds, $url);
			$category = $_arr[0];
			$topic = $_arr[1];
		}
		else {
			$category = $url;
			$topic = null;
		}
		
		// if hash is present
		if (strpos($topic, '#')) {
			$_arr = split('#', $topic);
			$topic = $_arr[0];
			$hash = '#' . $_arr[1];
		}
		else {
			$hash = blank;
		}
		
		// if toc
		if ($category == $this->ci->docs->config('toc_uri') OR $topic == $this->ci->docs->config('toc_uri')) {
			$category = blank;
			$topic = $this->ci->docs->config('toc_uri');
		}
		
		// if a root url
		$root_safe = array('home', 'index', '_root', ds);
		if ((is_null($category) OR in_array($category, $root_safe)) AND is_null($topic)) {
			$category = ($guide == $this->ci->docs->config('guide_url')) ? blank : ds;
		}
		
		// if not a root url add ds
		if ($category != blank AND $category != ds) {
			$category .= ds;
		}
		
		// generate url
		$url = $guide . $version . $category . $topic;
		
		// if we aren't using a version or different guide, set the title to the Category: Topic we are linking to
		if ($version == blank AND $guide == $this->ci->docs->config('guide_url') AND $category != ds) {
			$data = $this->_get_page_data($url, true);
			
			// fail gracefully: nothing found in TOC
			if ($data == false)
				return '<span title="The url ' . $url . ' was not found in the TOC" class="broken_link">' . $text . '</span>';
			
			if ($data['cat_id'] == 0) {
				$title = lang('shortcut_to') . sp . $data['name'];
			}
			else {
				$title = lang('shortcut_to') . sp . $data['cat_name'] . lang('category_topic_sep') . sp .  $data['name'];
			}
			
		}
		elseif ($guide != $this->ci->docs->config('guide_url')) {
			$title = lang('visit_the') . sp . $_guide . sp . lang('user_guide');
		}
		else {
			$title = blank;
		}
		
		// if we're in this local guide then add the suffix
		if ($guide == $this->ci->docs->config('guide_url')) {
			$url .= suffix;
		}
		
		$url .= $hash;
		
		return "<a href=\"$url\" title=\"$title\" target=\"$target\">$text</a>";
	}
	
	
	/**
	 * version_link ( $version = null, $url = null, $text = null, $target = null )
	 * 
	 * Creates a version link to a page
	 * 
	 * @access public
	 * @param string $version (default: null)
	 * @param string $url (default: null)
	 * @param string $text (default: null)
	 * @param string $target (default: null)
	 * @return string
	 */
	function version_link($version = null, $url = null, $text = null, $target = null) {
		return $this->link($url, $text, $target, $version);
	}
	
	
	/**
	 * guide_link ( $guide = null, $url = null, $text = null, $target = null )
	 * 
	 * Creates alink to another guide on the CI Docs install
	 * 
	 * @access public
	 * @param string $guide (default: null)
	 * @param string $url (default: null)
	 * @param string $text (default: null)
	 * @param string $target (default: null)
	 * @return string
	 */
	function guide_link($guide = null, $url = null, $text = null, $target = null) {
		return $this->link($url, $text, $target, null, $guide);
	}
	
	
	function get_license($name = null) {
		if (is_null($name))
			show_error('No license file specified.');
		
		return $this->ci->themes->block(ds . $this->ci->docs->config('licenses_path') . $name);
	}
	

}

/* End of file Toc.php */
/* Location: ./app/libraries/Toc.php */